package com.pp.web.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pp.common.pojo.Post;
import com.pp.common.pojo.PostTag;
import com.pp.common.pojo.Tag;
import com.pp.common.pojo.User;
import com.pp.web.mapper.PostMapper;
import com.pp.web.mapper.TagMapper;
import com.pp.web.mapper.UserMapper;
import com.pp.web.model.dto.CreatePostDTO;
import com.pp.web.model.dto.UpdatePostDTO;
import com.pp.web.model.vo.PostVO;
import com.pp.web.service.IPostService;
import com.pp.web.service.IPostTagService;
import com.pp.web.service.ITagService;
import com.pp.web.service.IUserService;
import com.vdurmont.emoji.EmojiParser;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements IPostService {

    @Resource
    private PostMapper postMapper;

    @Resource
    private UserMapper userMapper;
    @Resource
    private IUserService userService;

    @Resource
    private TagMapper tagMapper;
    @Resource
    private ITagService tagService;

    @Resource
    private IPostTagService postTagService;

    @Override
    public Page<PostVO> getPostsByPages(Page<PostVO> page, String tabName) {
        // 查询帖子 =>
        // 因为 PostMapper 已经被代理，所以此处可以使用 this.baseMapper
        // this.baseMapper 接口的底层实现类就是泛型指定的 PostMapper
        // pageResult:
        Page<PostVO> pageResult = this.baseMapper.findPostsByPages(page, tabName);

        // 查询帖子的标签 =>
        this.setPostTags(pageResult);

        // 返回设置好了
        return pageResult;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Post createPost(CreatePostDTO createPostDTO, User user) {
        // 帖子的 title 必须唯一 =>
        Post data = this.baseMapper.selectOne(
                new LambdaQueryWrapper<Post>().eq(Post::getTitle, createPostDTO.getTitle())
        );
        Assert.isNull(data, "帖子已存在，请修改");

        // 若帖子 title 不存在，则封装当前 topic 对象 =>
        Post newPost = Post.builder()
                        .userId(user.getId()) // userId
                        .title(EmojiParser.parseToAliases(createPostDTO.getTitle())) // title
                        // 解析 title 和 content 中的 emoji
                        .content(EmojiParser.parseToAliases(createPostDTO.getContent())) // content
                        .createdUser(user.getUsername()) // createdTime
                        .updatedUser(user.getUsername()) // updatedTime
                        .build();

        // 将当前封装好的 newPost 对象入库 =>
        this.baseMapper.insert(newPost);

        // 更新 user 的发帖量 postNum 和 积分 point 信息 =>
        // 增加用户发帖量
        int newPostNum = user.getPostNum() + 1;
        // 增加用户积分 (发一篇新帖，积分 + 1)
        int newPoint = user.getPoint() + 1;
        user.setPostNum(newPostNum).setPoint(newPoint);
        this.userMapper.updateById(user);

        // 帖子标签信息也入库 =>
        if (!ObjectUtils.isEmpty(createPostDTO.getTags())) {
            // 具体标签信息入库 =>
            List<Tag> tags = tagService.createTags(createPostDTO.getTags(), user);
            // 帖子和标签的关系对也入库 =>
            postTagService.createPostTag(newPost.getId(), tags);
        }

        return newPost;
    }

    @Override
    public Map<String, Object> getPostById(String postId) {
        // map 中存放 post, tags, user
        Map<String, Object> responseMap = new HashMap<>(16);

        // 若当前帖子信息不存在 =>
        Post post = this.baseMapper.selectById(postId);
        Assert.notNull(post, "当前帖子不存在，或已被作者删除");

        // 若当前帖子已经存在 =>
        // 浏览量 + 1
        post.setViewNum(post.getViewNum() + 1);
        this.baseMapper.updateById(post);

        // 帖子具体内容中的 Emoji 转码
        post.setContent(EmojiParser.parseToUnicode(post.getContent()));
        responseMap.put("post", post);

        // 获取当前帖子对应的标签
        QueryWrapper<PostTag> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(PostTag::getPostId, post.getId());
        List<PostTag> postTags = postTagService.list(queryWrapper);
        Set<String> tagIds = new HashSet<>();
        // 根据 topicId 在 topic 与 tag 关联表中查询到所有的 tagIds
        for (PostTag postTag : postTags) {
            tagIds.add(postTag.getTagId());
        }

        // 根据从关联表中查询出来的 tagIds 获得具体的 tags 信息
        List<Tag> tags = tagService.listByIds(tagIds);
        responseMap.put("tags", tags);

        // 作者
        User author = userService.getUserById(post.getUserId());
        responseMap.put("author", author);

        return responseMap;
    }

    @Override
    public Page<PostVO> searchPostsByKeyword(String keyword, Page<PostVO> page) {
        // TODO 目前只实现了根据标题查询
        // 查询帖子 =>
        Page<PostVO> pageResult = this.baseMapper.searchPostsByKeyword(page, keyword);

        // 查询帖子的标签 =>
        this.setPostTags(pageResult);

        // 返回设置好标签的分页结果对象 pageResult
        return pageResult;
    }

    @Override
    public Post updatePost(String username, UpdatePostDTO updatePostDTO) {
        // Step 1: 更新帖子 (title 和 content) =>
        // 根据 username 获取用户名
        User user = userService.getUserByUsername(username);

        // 获取 updatePostDTO 中的 post
        Post post = updatePostDTO.getPost();

        // 判断当前用户是否为当前要修改帖子的作者，若不是则提示 "非本人无权修改"
        Assert.isTrue(user.getId().equals(post.getUserId()), "非本人无权修改");

        System.out.println("---------- check post object: " + post);

        // 若当前用户是当前要修改帖子的作者，则执行修改帖子操作
        // Emoji 转码
        post.setTitle(EmojiParser.parseToAliases(post.getTitle()))
            .setContent(EmojiParser.parseToAliases(post.getContent()));

        // post 对象中包含 postId 信息，所以此处根据 id 修改
        this.postMapper.updateById(post);

        // Step 2: 更新帖子对应的标签 =>
        // 更新帖子前先根据 postId 刪掉 post 与 tag 的所有关联信息
        this.postTagService.remove(
             new LambdaQueryWrapper<PostTag>().eq(PostTag::getPostId, post.getId())
        );

        // 获取前端传过来的 tags (tagNames)
        List<String> tagNames = updatePostDTO.getTags();

        // 遍历每个 tagName (tagName 分两种情况: 已经在 tag 表中存在，或还不存在)
        for(String tagName: tagNames) {
            // 根据 tagName 查询是否存在对应的 tag (创建 LambdaQueryWrapper 时一定要添加对应的泛型)
            Tag tag = this.tagMapper.selectOne(
                new LambdaQueryWrapper<Tag>().eq(Tag::getTagName, tagName)
            );

            // 若当前 tagName 对应的 tag 已经存在，则:
            // 1. 不用操作 tag 表
            // 2. 只用操作 post_tag 表，维护 post 与 tag 的关系
            if(!ObjectUtils.isEmpty(tag)) {
                PostTag newPostTag = new PostTag().setPostId(post.getId()).setTagId(tag.getId());
                this.postTagService.save(newPostTag);
                continue;
            }

            // 若当前 tagName 对应的 tag 不存在，则:
            // 1. 操作 tag 表，将 newTag 入库
            // 2. 同时也要操作 post_tag 表，维护 post 与 tag 的关系
            Tag newTag = Tag.builder()
                            .tagName(tagName)
                            .createdUser(user.getUsername())
                            .updatedUser(user.getUsername())
                            .build();
            this.tagMapper.insert(newTag);

            PostTag newPostTag = new PostTag().setPostId(post.getId()).setTagId(newTag.getId());
            this.postTagService.save(newPostTag);
        }

        return post;
    }

    /**
     * getPostsByPages 方法调用的根据 topicId 获取对应 tags 的辅助方法
     */
    private void setPostTags(Page<PostVO> pageResult) {
        // pageResult.records 中的每一个 record 都是一个 PostVO 综合帖子对象
        pageResult.getRecords().forEach(post -> {
            // 对于每一个 postId 和 tagId 关联对
            List<PostTag> postTags = postTagService.findByPostId(post.getPostId());
            // 若帖子有其对应标签
            if (!postTags.isEmpty()) {
                // 从每一个 postId 和 tagId 对中拿到 tagId
                // topicId  tagId
                // 1        2
                // 1        4
                // 1        5
                // topicTags List : [(1, 2), (1, 4), (1, 5)]
                // after map method: 2, 4, 5
                // after collect method: [2, 4, 5]
                List<String> tagIds = postTags.stream()
                                              .map(PostTag::getTagId)
                                              .collect(Collectors.toList());

                // 进而拿到 tagIds 对应的具体标签信息
                List<Tag> tags = tagMapper.selectBatchIds(tagIds);

                // 将 tags 保存到当前的 postVO.tags 中
                post.setTags(tags);
            }
        });
    }
}
